Skip to content

[Detail Bug] Address blocklist bypass via invalid mixed-case checksum input #172

@detail-app

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_ea7bf3e3-a2f4-4402-9351-baa0e1eaa1f5/bugs/bug_faa4d7de-7f12-43e0-a682-8e8665f51ae7

Summary

  • Context: isBlockedAddress is a security/policy utility used (e.g., in EventManager) to filter out events from non-user addresses like the zero address or the dead address.
  • Bug: The function uses strict EIP-55 checksum validation on its input, allowing blocked addresses to escape the blocklist if they are provided with an invalid mixed-case checksum.
  • Actual vs. expected: A blocked address with an incorrect mixed-case checksum is not recognized as blocked (false), whereas it should be blocked regardless of casing or checksum validity.
  • Impact: The blocklist can be easily bypassed by any user or automated process, leading to "dirty" analytics data or potential spam from addresses that were intended to be filtered out.

Code with Bug

export const isBlockedAddress = (
  address: Address | null | undefined
): boolean => {
  if (!address) return false;

  const validAddress = getValidAddress(address); // <-- BUG 🔴 [Enforces strict checksum for mixed-case, returning null if invalid]
  if (!validAddress) return false; // <-- BUG 🔴 [Returns false immediately, bypassing the block check]

  // Normalize to checksum format for comparison
  const checksumAddress = toChecksumAddress(validAddress);

  return BLOCKED_ADDRESSES.some(
    (blockedAddr) => toChecksumAddress(blockedAddr) === checksumAddress
  );
};

Explanation

isBlockedAddress first calls getValidAddress. For mixed-case addresses, getValidAddress enforces EIP-55 checksum correctness and returns null when the checksum is invalid. isBlockedAddress then immediately returns false when validAddress is null, so it never compares against BLOCKED_ADDRESSES.

This allows a blocked address (e.g., the dead address) to bypass the blocklist by using an intentionally wrong mixed-case checksum (e.g., changing ...dEaD to ...dEAD). The same address provided as all-lowercase is correctly blocked.

Codebase Inconsistency

The underlying isAddress behavior allows all-lowercase and all-uppercase addresses without checksum validation, but enforces strict checksum validation for mixed-case input. isBlockedAddress inherits this behavior, making blocklist enforcement depend on checksum validity rather than address identity.

Recommended Fix

The isBlockedAddress function should be robust against casing variations and should support both EVM and Solana addresses (by utilizing isBlockedSolanaAddress as well).

export const isBlockedAddress = (
  address: Address | null | undefined
): boolean => {
  // <-- FIX 🟢
  if (!address || typeof address !== "string") return false;

  const trimmed = address.trim().toLowerCase();

  // Check EVM blocklist (case-insensitive)
  if (BLOCKED_ADDRESSES.some((b) => b.toLowerCase() === trimmed)) {
    return true;
  }

  // Also check Solana blocked addresses
  if (isSolanaAddress(trimmed) && isBlockedSolanaAddress(trimmed)) {
    return true;
  }

  return false;
};

History

This bug was introduced in commit 7374bb4. The commit added address blocking logic that relies on getValidAddress, which returns null for addresses with invalid checksums, allowing blocked addresses to bypass the check by using mixed-case characters.

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