From d7e9fa486add47eeb644e69014b3dffd2bd7ac8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Tue, 3 Sep 2024 17:35:01 +0200 Subject: [PATCH 1/2] fix(#1894): make response of the metadata restricted to be raw text --- .../metadata-validation/src/app.service.ts | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/govtool/metadata-validation/src/app.service.ts b/govtool/metadata-validation/src/app.service.ts index d1bdaa90f..f42afb111 100644 --- a/govtool/metadata-validation/src/app.service.ts +++ b/govtool/metadata-validation/src/app.service.ts @@ -14,6 +14,7 @@ const axiosConfig: AxiosRequestConfig = { timeout: 5000, maxContentLength: 10 * 1024 * 1024, // Max content length 10MB maxBodyLength: 10 * 1024 * 1024, // Max body length 10MB + responseType: 'text', }; @Injectable() @@ -28,7 +29,7 @@ export class AppService { let metadata: Record; try { - const { data } = await firstValueFrom( + const { data: rawData } = await firstValueFrom( this.httpService.get(url, axiosConfig).pipe( timeout(5000), catchError(() => { @@ -37,38 +38,44 @@ export class AppService { ), ); - if (!data?.body) { + const parsedData = JSON.parse(rawData); + + if (!parsedData?.body) { throw MetadataValidationStatus.INCORRECT_FORMAT; } - const standard = getStandard(data); + const standard = getStandard(parsedData); if (standard) { - await validateMetadataStandard(data.body, standard); - metadata = parseMetadata(data.body); + await validateMetadataStandard(parsedData.body, standard); + metadata = parseMetadata(parsedData.body); } - const hashedMetadata = blake.blake2bHex( - JSON.stringify(data), - undefined, - 32, - ); + const hashedMetadata = blake.blake2bHex(rawData, undefined, 32); if (hashedMetadata !== hash) { - // Optional support for the canonized data hash - // Validate canonized data hash - const canonizedMetadata = await jsonld.canonize(data, { - safe: false, - }); - - const hashedCanonizedMetadata = blake.blake2bHex( - canonizedMetadata, + // Optionally validate on a parsed metadata + const hashedParsedMetadata = blake.blake2bHex( + JSON.stringify(parsedData), undefined, 32, ); + if (hashedParsedMetadata !== hash) { + // Optional support for the canonized data hash + // Validate canonized data hash + const canonizedMetadata = await jsonld.canonize(JSON.parse(rawData), { + safe: false, + }); + + const hashedCanonizedMetadata = blake.blake2bHex( + canonizedMetadata, + undefined, + 32, + ); - if (hashedCanonizedMetadata !== hash) { - throw MetadataValidationStatus.INVALID_HASH; + if (hashedCanonizedMetadata !== hash) { + throw MetadataValidationStatus.INVALID_HASH; + } } } } catch (error) { From 3b53f3d1987ec8898ee3be1497e5dfec0921c2ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Tue, 3 Sep 2024 18:29:14 +0200 Subject: [PATCH 2/2] fix(#1894): fix metadata hash generation --- .../DashboardCards/DRepDashboardCard.tsx | 10 +++++++- .../src/context/governanceAction.test.tsx | 2 +- .../frontend/src/context/governanceAction.tsx | 6 ++++- .../forms/useCreateGovernanceActionForm.ts | 2 +- .../src/hooks/forms/useEditDRepInfoForm.ts | 2 +- .../src/hooks/forms/useRegisterAsdRepForm.tsx | 3 +-- .../src/hooks/forms/useVoteContextForm.tsx | 2 +- govtool/frontend/src/i18n/locales/en.ts | 12 ++++++---- .../metadata-validation/src/app.service.ts | 7 +++++- .../src/utils/validateCIP108body.ts | 24 +++++-------------- 10 files changed, 38 insertions(+), 32 deletions(-) diff --git a/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx b/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx index 5afc070e5..9fe20bbc9 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx @@ -59,7 +59,15 @@ export const DRepDashboardCard = ({ title: t("dashboard.cards.drep.dRepRegistration"), }), ...(pendingTransaction.retireAsDrep && { - description: ( + description: voter?.givenName ? ( + + ) : ( { const hash = await createHash(jsonld!); expect(hash).toBeDefined(); expect(hash).toBe( - "bbdbbe163d1b8e4d6c10180df515cc7d58109412d90a42e898fc66850b3fc98c", + "72b37e2f5e64e7de57b85558ba00885c848f700fbb37fbed3197e603873fa976", ); }; test(); diff --git a/govtool/frontend/src/context/governanceAction.tsx b/govtool/frontend/src/context/governanceAction.tsx index 807a22608..d8f790b9c 100644 --- a/govtool/frontend/src/context/governanceAction.tsx +++ b/govtool/frontend/src/context/governanceAction.tsx @@ -76,7 +76,11 @@ const GovernanceActionProvider = ({ children }: PropsWithChildren) => { */ const createHash = useCallback(async (jsonLD: NodeObject) => { try { - const jsonHash = blake2bHex(JSON.stringify(jsonLD), undefined, 32); + const jsonHash = blake2bHex( + JSON.stringify(jsonLD, null, 2), + undefined, + 32, + ); return jsonHash; } catch (error) { Sentry.captureException(error); diff --git a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts index e6709a4a5..73f05ec0a 100644 --- a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts +++ b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts @@ -109,7 +109,7 @@ export const useCreateGovernanceActionForm = ( const jsonld = await generateJsonld(body, GOVERNANCE_ACTION_CONTEXT); - const jsonHash = blake2bHex(JSON.stringify(jsonld), undefined, 32); + const jsonHash = blake2bHex(JSON.stringify(jsonld, null, 2), undefined, 32); // That allows to validate metadata hash setHash(jsonHash); diff --git a/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts index 904d79ba6..7dc1b6346 100644 --- a/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts +++ b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts @@ -100,7 +100,7 @@ export const useEditDRepInfoForm = ( const jsonld = await generateJsonld(body, DREP_CONTEXT, CIP_119); - const jsonHash = blake2bHex(JSON.stringify(jsonld), undefined, 32); + const jsonHash = blake2bHex(JSON.stringify(jsonld, null, 2), undefined, 32); setHash(jsonHash); setJson(jsonld); diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx index b737eda58..df8045594 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -109,10 +109,9 @@ export const useRegisterAsdRepForm = ( ], standardReference: CIP_119, }); - const jsonld = await generateJsonld(body, DREP_CONTEXT, CIP_119); - const jsonHash = blake2bHex(JSON.stringify(jsonld), undefined, 32); + const jsonHash = blake2bHex(JSON.stringify(jsonld, null, 2), undefined, 32); setHash(jsonHash); setJson(jsonld); diff --git a/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx b/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx index 9dc004a0c..11779489d 100644 --- a/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx +++ b/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx @@ -47,7 +47,7 @@ export const useVoteContextForm = ( }); const jsonld = await generateJsonld(body, CIP_100_CONTEXT, CIP_100); - const jsonHash = blake2bHex(JSON.stringify(jsonld), undefined, 32); + const jsonHash = blake2bHex(JSON.stringify(jsonld, null, 2), undefined, 32); // That allows to validate metadata hash setHash(jsonHash); diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index 44a8c57a8..ad39c7410 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -85,7 +85,9 @@ export const en = { reRegister: "Re-register as a DRep", retire: "Retire as a DRep", retirementInProgress: - "You are being retired as MrDRep. You will receive a refund of {{deposit}} ADA when the transaction completes.", + "You are being retired. You will receive a refund of {{deposit}} ADA when the transaction completes.", + retirementInProgressWithGivenName: + "You are being retired as {{givenName}}. You will receive a refund of {{deposit}} ADA when the transaction completes.", viewDetails: "View your DRep details", youAreRegistered: "You are Registered as a DRep", yourDRepId: "Your DRep ID", @@ -361,7 +363,8 @@ export const en = { }, dRepData: { givenName: "DRep Name", - givenNameHelpfulText: "This is the name that will be shown on your DRep profile", + givenNameHelpfulText: + "This is the name that will be shown on your DRep profile", objectives: "Objectives", objectivesHelpfulText: "What you believe and what you want to achieve as a DRep.", @@ -391,10 +394,9 @@ export const en = { }, references: "References", referenceDescription: "Description", - referenceDescriptionHelpfulText: - "Limit: 80 characters", + referenceDescriptionHelpfulText: "Limit: 80 characters", referenceURL: "URL", - }, + }, errors: { tooLongUrl: "Url must be less than 128 bytes", mustBeStakeAddress: "It must be reward address in bech32 format", diff --git a/govtool/metadata-validation/src/app.service.ts b/govtool/metadata-validation/src/app.service.ts index f42afb111..86c66e13e 100644 --- a/govtool/metadata-validation/src/app.service.ts +++ b/govtool/metadata-validation/src/app.service.ts @@ -38,7 +38,12 @@ export class AppService { ), ); - const parsedData = JSON.parse(rawData); + let parsedData; + try { + parsedData = JSON.parse(rawData); + } catch (error) { + throw MetadataValidationStatus.INCORRECT_FORMAT; + } if (!parsedData?.body) { throw MetadataValidationStatus.INCORRECT_FORMAT; diff --git a/govtool/metadata-validation/src/utils/validateCIP108body.ts b/govtool/metadata-validation/src/utils/validateCIP108body.ts index a9bae7157..abd8bc70b 100644 --- a/govtool/metadata-validation/src/utils/validateCIP108body.ts +++ b/govtool/metadata-validation/src/utils/validateCIP108body.ts @@ -1,16 +1,7 @@ -import * as Joi from 'joi'; - import { MetadataValidationStatus } from '@/enums'; import { getFieldValue } from './getFieldValue'; -const CIP108FieldRules = { - title: Joi.string().allow('').max(80), - abstract: Joi.string().allow('').max(2500), - motivation: Joi.string().allow(''), - rationale: Joi.string().allow(''), -}; - /** * Validates the body of a CIP108 standard. * @@ -23,15 +14,12 @@ export const validateCIP108body = (body: Record) => { const abstract = getFieldValue(body, 'abstract'); const motivation = getFieldValue(body, 'motivation'); const rationale = getFieldValue(body, 'rationale'); - - try { - CIP108FieldRules.title.validate(title); - CIP108FieldRules.abstract.validate(abstract); - CIP108FieldRules.motivation.validate(motivation); - CIP108FieldRules.rationale.validate(rationale); - - return true; - } catch (error) { + if (!title || !abstract || !motivation || !rationale) { + throw MetadataValidationStatus.INCORRECT_FORMAT; + } + if (String(title).length > 80 || String(abstract).length > 2500) { throw MetadataValidationStatus.INCORRECT_FORMAT; } + + return true; };