Skip to content

subscribe() accepts zero, negative, or invalid subscription periods #314

Description

@mssystem1

Describe the bug

subscribe() validates recurringCharge and subscriptionOwner, but it does not clearly validate periodInDays or overridePeriodInSecondsForTestnet before creating spend-permission typed data. This can allow invalid subscription periods such as 0, negative numbers, non-integers, or unsafe numbers to reach wallet signing / typed-data generation.

Steps

  1. Open packages/account-sdk/src/interface/payment/subscribe.ts
  2. Check subscribe()
  3. Notice that recurringCharge and subscriptionOwner are validated
  4. Notice that periodInDays and overridePeriodInSecondsForTestnet are not validated as positive safe integers
  5. Try calling subscribe() with invalid periods:
await subscribe({
  recurringCharge: "1.00",
  subscriptionOwner,
  periodInDays: 0,
  testnet: true,
});

await subscribe({
  recurringCharge: "1.00",
  subscriptionOwner,
  periodInDays: -1 as any,
  testnet: true,
});

await subscribe({
  recurringCharge: "1.00",
  subscriptionOwner,
  overridePeriodInSecondsForTestnet: 0,
  testnet: true,
});

Expected behavior

subscribe() should reject invalid period values before requesting wallet signing.

It should require:

  • periodInDays to be a positive safe integer
  • overridePeriodInSecondsForTestnet to be a positive safe integer
  • the final period value to fit inside the expected uint48 range

Version

2.5.6 / latest master

Additional info

Suggested fix: add runtime validation before typed data creation.

Example:

function validatePositiveSafeInteger(value: number, fieldName: string): void {
  if (!Number.isSafeInteger(value) || value <= 0) {
    throw new Error(`${fieldName} must be a positive safe integer`);
  }
}

function validateUint48(value: number, fieldName: string): void {
  const maxUint48 = 2 ** 48 - 1;

  if (value > maxUint48) {
    throw new Error(`${fieldName} must fit within uint48`);
  }
}

Then validate before creating typed data:

validatePositiveSafeInteger(periodInDays, 'periodInDays');
validateUint48(86400 * periodInDays, 'periodInDays');

if (overridePeriodInSecondsForTestnet !== undefined) {
  validatePositiveSafeInteger(
    overridePeriodInSecondsForTestnet,
    'overridePeriodInSecondsForTestnet'
  );

  validateUint48(
    overridePeriodInSecondsForTestnet,
    'overridePeriodInSecondsForTestnet'
  );
}

Desktop

No response

Smartphone

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions