From 4ff3dc483749fce21521a8feed1e9b017a5630b6 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Wed, 27 Dec 2023 13:36:37 -0700 Subject: [PATCH 1/2] add limit to vec of empty --- .../simple_candid_definition_arb.ts | 2 +- .../candid/constructed/vec_arb/values_arb.ts | 65 ++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/property_tests/arbitraries/candid/candid_definition_arb/simple_candid_definition_arb.ts b/property_tests/arbitraries/candid/candid_definition_arb/simple_candid_definition_arb.ts index fab1c4186a..0e24c256b7 100644 --- a/property_tests/arbitraries/candid/candid_definition_arb/simple_candid_definition_arb.ts +++ b/property_tests/arbitraries/candid/candid_definition_arb/simple_candid_definition_arb.ts @@ -58,7 +58,7 @@ export function primitiveCandidDefinitionArb( { arbitrary: Nat16DefinitionArb(), weight: constraints.nat16 ?? 1 }, { arbitrary: Nat32DefinitionArb(), weight: constraints.nat32 ?? 1 }, { arbitrary: Nat64DefinitionArb(), weight: constraints.nat64 ?? 1 }, - { arbitrary: NullDefinitionArb(), weight: constraints.null ?? 0 }, // TODO Null must be excluded until https://github.com/demergent-labs/azle/issues/1453 gets resolved + { arbitrary: NullDefinitionArb(), weight: constraints.null ?? 1 }, { arbitrary: TextDefinitionArb(), weight: constraints.text ?? 1 }, { arbitrary: PrincipalDefinitionArb(), diff --git a/property_tests/arbitraries/candid/constructed/vec_arb/values_arb.ts b/property_tests/arbitraries/candid/constructed/vec_arb/values_arb.ts index 202430e80d..3422c4e7f1 100644 --- a/property_tests/arbitraries/candid/constructed/vec_arb/values_arb.ts +++ b/property_tests/arbitraries/candid/constructed/vec_arb/values_arb.ts @@ -2,7 +2,14 @@ import fc from 'fast-check'; import { Vec } from '.'; import { CandidType } from '../../candid_type'; import { CorrespondingJSType } from '../../corresponding_js_type'; -import { VecCandidDefinition } from '../../candid_definition_arb/types'; +import { + CandidDefinition, + OptCandidDefinition, + RecordCandidDefinition, + TupleCandidDefinition, + VariantCandidDefinition, + VecCandidDefinition +} from '../../candid_definition_arb/types'; import { CandidValues, CandidValueArb } from '../../candid_values_arb'; import { RecursiveShapes } from '../../recursive'; @@ -16,6 +23,11 @@ export function VecValuesArb( generateEmptyVec(vecDefinition.innerType.candidMeta.candidType) ); } + if (isEmptyInnerType(vecDefinition.innerType)) { + return fc.constant( + generateEmptyVec(vecDefinition.innerType.candidMeta.candidType) + ); + } const arbitraryMemberValues = fc .tuple( fc.array(fc.constant(null)), @@ -148,3 +160,54 @@ function typeArray( return arr; } + +/** + * In order to prevent DoS attacks empty inner types can only be of length 0. + * Empty types are null, empty record, empty tuple, record with one field that + * is a null, tuple with one field that is a null. + * Lets start with a vec or null, or vec or tuple where all the fields are null, + * or vec or record where all things are null or vec or variant where things are + * null + * https://github.com/demergent-labs/azle/issues/1524 + * @param innerType + * @returns + */ +function isEmptyInnerType(innerType: CandidDefinition): boolean { + if (innerType.candidMeta.candidType === 'Null') { + return true; + } + if ( + innerType.candidMeta.candidType === 'Record' || + innerType.candidMeta.candidType === 'Tuple' || + innerType.candidMeta.candidType === 'Variant' || + innerType.candidMeta.candidType === 'Opt' + ) { + return areAllFieldsNull(innerType); + } + return false; +} + +function areAllFieldsNull(innerType: CandidDefinition): boolean { + if ( + innerType.candidMeta.candidType === 'Record' || + innerType.candidMeta.candidType === 'Variant' + ) { + const multiInnerType = innerType as + | RecordCandidDefinition + | VariantCandidDefinition; + return multiInnerType.innerTypes.every((innerType) => + isEmptyInnerType(innerType[1]) + ); + } + if (innerType.candidMeta.candidType === 'Tuple') { + const multiInnerType = innerType as TupleCandidDefinition; + return multiInnerType.innerTypes.every((innerType) => + isEmptyInnerType(innerType) + ); + } + if (innerType.candidMeta.candidType === 'Opt') { + const optInnerType = innerType as OptCandidDefinition; + return isEmptyInnerType(optInnerType.innerType); + } + return false; +} From 554b7ce67da6fb5cfd6190d5096f9660a3e78aa9 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Wed, 27 Dec 2023 14:12:39 -0700 Subject: [PATCH 2/2] update to limit size instead of forcing it to be empty --- .../candid/constructed/vec_arb/values_arb.ts | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/property_tests/arbitraries/candid/constructed/vec_arb/values_arb.ts b/property_tests/arbitraries/candid/constructed/vec_arb/values_arb.ts index 3422c4e7f1..1273433097 100644 --- a/property_tests/arbitraries/candid/constructed/vec_arb/values_arb.ts +++ b/property_tests/arbitraries/candid/constructed/vec_arb/values_arb.ts @@ -1,4 +1,4 @@ -import fc from 'fast-check'; +import fc, { ArrayConstraints } from 'fast-check'; import { Vec } from '.'; import { CandidType } from '../../candid_type'; import { CorrespondingJSType } from '../../corresponding_js_type'; @@ -13,6 +13,14 @@ import { import { CandidValues, CandidValueArb } from '../../candid_values_arb'; import { RecursiveShapes } from '../../recursive'; +/* +https://github.com/dfinity/candid/blob/491969f34dd791e51f69c5f8d3c6192ae405b839/spec/Candid.md#memory +Set size limit to follow candid spec +const NULL_VEC_SIZE_LIMIT = 2_000_000; +*/ +// TODO set to zero until the limit is unified on both the canister and client side as per https://github.com/demergent-labs/azle/issues/1538 +const EMPTYISH_VEC_SIZE_LIMIT = 0; + export function VecValuesArb( vecDefinition: VecCandidDefinition, recursiveShapes: RecursiveShapes, @@ -23,14 +31,9 @@ export function VecValuesArb( generateEmptyVec(vecDefinition.innerType.candidMeta.candidType) ); } - if (isEmptyInnerType(vecDefinition.innerType)) { - return fc.constant( - generateEmptyVec(vecDefinition.innerType.candidMeta.candidType) - ); - } const arbitraryMemberValues = fc .tuple( - fc.array(fc.constant(null)), + fc.array(fc.constant(null), determineVecConstraints(vecDefinition)), fc.constant(vecDefinition.innerType) ) .chain(([arrayTemplate, innerType]) => @@ -59,6 +62,15 @@ export function VecValuesArb( }); } +function determineVecConstraints( + vecDefinition: VecCandidDefinition +): ArrayConstraints | undefined { + if (isEmptyInnerType(vecDefinition.innerType)) { + return { maxLength: EMPTYISH_VEC_SIZE_LIMIT }; + } + return; +} + function generateEmptyVec(innerCandidType: CandidType): CandidValues { return { valueLiteral: typeValueLiteral('[]', innerCandidType),