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..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,11 +1,26 @@ -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'; -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'; +/* +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, @@ -18,7 +33,7 @@ export function VecValuesArb( } const arbitraryMemberValues = fc .tuple( - fc.array(fc.constant(null)), + fc.array(fc.constant(null), determineVecConstraints(vecDefinition)), fc.constant(vecDefinition.innerType) ) .chain(([arrayTemplate, innerType]) => @@ -47,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), @@ -148,3 +172,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; +}