Skip to content

Commit

Permalink
Merge pull request #385 from Concordium/validate-integer-types-correctly
Browse files Browse the repository at this point in the history
Allow bigints as type integer
  • Loading branch information
orhoj authored Sep 6, 2023
2 parents dcb7c8f + 84c4f36 commit bb2c756
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 7 deletions.
1 change: 1 addition & 0 deletions packages/browser-wallet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Changed 'Zero Knowledge' to 'Zero-knowledge' in display texts.
- An issue with images in verifiable credentials for lower resolutions.
- Schema validation of verifiable credentials with attributes that have `{ type: "integer" }` no longer rejects BigInt values.

## 1.1.6

Expand Down
29 changes: 24 additions & 5 deletions packages/browser-wallet/src/background/web3Id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
IdStatement,
StatementTypes,
AttributeType,
isTimestampAttribute,
TimestampAttribute,
} from '@concordium/web-sdk';
import {
sessionVerifiableCredentials,
Expand Down Expand Up @@ -126,12 +128,21 @@ function rejectRequest(message: string): { run: false; response: MessageStatusWr
};
}

// TODO Expose function from SDK and re-use.
function timestampToDate(attribute: TimestampAttribute): Date {
return new Date(Date.parse(attribute.timestamp));
}

function validateTimestampAttribute(attributeTag: string, attributeValue: AttributeType) {
if (
attributeValue instanceof Date &&
(attributeValue.getTime() < MIN_DATE_TIMESTAMP || attributeValue.getTime() > MAX_DATE_TIMESTAMP)
) {
return `The attribute [${attributeValue}] for key [${attributeTag}] is out of bounds for a Date. The Date must be between ${MIN_DATE_ISO} and ${MAX_DATE_ISO}`;
if (isTimestampAttribute(attributeValue)) {
const timestamp = timestampToDate(attributeValue).getTime();
if (Number.isNaN(timestamp)) {
return `The attribute [${attributeValue.timestamp}] for key [${attributeTag}] cannot be parsed as a Date.`;
}

if (timestamp < MIN_DATE_TIMESTAMP || timestamp > MAX_DATE_TIMESTAMP) {
return `The attribute [${attributeValue.timestamp}] for key [${attributeTag}] is out of bounds for a Date. The Date must be between ${MIN_DATE_ISO} and ${MAX_DATE_ISO}`;
}
}
return undefined;
}
Expand All @@ -154,6 +165,14 @@ function validateAttributeBounds(
attributeTag: string,
attributeValue: AttributeType
): { error: false } | { error: true; message: string } {
if (
typeof attributeValue !== 'string' &&
typeof attributeValue !== 'bigint' &&
!isTimestampAttribute(attributeValue)
) {
return { error: true, message: 'Unsupported attribute type' };
}

const stringError = validateStringAttribute(attributeTag, attributeValue);
if (stringError) {
return { error: true, message: stringError };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useAsyncMemo } from 'wallet-common-helpers';
import { useHdWallet } from '@popup/shared/utils/account-helpers';
import { displayUrl } from '@popup/shared/utils/string-helpers';
import {
coerceBigIntIntegerToNumber,
createCredentialId,
createPublicKeyIdentifier,
fetchCredentialMetadata,
Expand Down Expand Up @@ -123,7 +124,8 @@ export default function AddWeb3IdCredential({ onAllow, onReject }: Props) {
const schemaWithNoId = withIdRemovedFromSchema(schema);
const validationResult = validator.validate(
{ credentialSubject: credential.credentialSubject },
schemaWithNoId as unknown as Schema
schemaWithNoId as unknown as Schema,
{ preValidateProperty: coerceBigIntIntegerToNumber }
);
if (!validationResult.valid) {
setError(t('error.schemaValidation', { errors: validationResult.errors.toString() }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
VerifiableCredentialStatus,
} from '@shared/storage/types';
import { Buffer } from 'buffer/';
import jsonschema from 'jsonschema';
import jsonschema, { Schema } from 'jsonschema';
import { applyExecutionNRGBuffer, getContractName } from './contract-helpers';
import { getNet } from './network-helpers';
import { logError } from './log-helpers';
Expand Down Expand Up @@ -1236,3 +1236,21 @@ export function withIdRemovedFromSchema(schema: VerifiableCredentialSchema) {

return { ...schema, properties: schemaOuterPropertiesWithNoId };
}

/**
* BigInts do not validate against a json schema as being an integer. This pre validate function
* coerces any bigint values where { type: "integer" } into number, as that will ensure that
* they validate against the schema.
*/
export function coerceBigIntIntegerToNumber(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
instance: any,
key: string,
schema: Schema
) {
const value = instance[key];
if (schema.type && schema.type === 'integer' && typeof value === 'bigint') {
// eslint-disable-next-line no-param-reassign
instance[key] = Number(value);
}
}
20 changes: 20 additions & 0 deletions packages/browser-wallet/test/verifiable-credential-helpers.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Schema, Validator } from 'jsonschema';
import {
RevocationDataHolder,
RevokeCredentialHolderParam,
Expand All @@ -13,6 +14,7 @@ import {
getCredentialIdFromSubjectDID,
getContractAddressFromIssuerDID,
getVerifiableCredentialPublicKeyfromSubjectDID,
coerceBigIntIntegerToNumber,
} from '../src/shared/utils/verifiable-credential-helpers';
import { mainnet, testnet } from '../src/shared/constants/networkConfiguration';

Expand Down Expand Up @@ -184,3 +186,21 @@ test('getCredentialIdFromSubjectDID extracts credId without network', () => {
'aad98095db73b5b22f7f64823a495c6c57413947353646313dc453fa4604715d2f93b2c1f8cb4c9625edd6330e1d27fa'
);
});

test('bigint with type integer validates against schema when coercing in pre validate', () => {
const validator = new Validator();
const schema: Schema = {
type: 'object',
properties: {
age: { type: 'integer' },
},
};

const p = {
age: BigInt(97),
};

const validationResult = validator.validate(p, schema, { preValidateProperty: coerceBigIntIntegerToNumber });

expect(validationResult.valid).toBeTruthy();
});

0 comments on commit bb2c756

Please sign in to comment.